﻿using Sustainalytics.OAuth.Services.Common;
using Sustainalytics.Portfolios.BL;
using Sustainalytics.Portfolios.BL.Exceptions;
using Sustainalytics.Portfolios.BL.Model;
using Sustainalytics.Portfolios.Service.Models;
using Sustainalytics.Utils;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace Sustainalytics.Portfolios.Service.Controllers
{
    using Sustainalytics.Entities;
    using System.Web.Http.Cors;
    using WebApi.OutputCache.V2;

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/systemportfolios")]
    public class SystemPortfolioController : ApiController
    {
        private readonly SystemPortfolioDtoCreator _portfolioDtoCreator;

        private readonly IPortfolioReadHandler _portfolioReadHandler;

        private readonly IManualSystemPortfolioUpdatesHandler _portfolioUpdateHandler;

        private readonly PortfolioUserClaimsProcessor _userClaimsProcessor;

        public SystemPortfolioController(
            IPortfolioReadHandler portfolioReadHandler,
            SystemPortfolioDtoCreator portfolioDtoCreator,
            IManualSystemPortfolioUpdatesHandler portfolioUpdateHandler,
            PortfolioUserClaimsProcessor userClaimsProcessor)
        {
            this._portfolioReadHandler = portfolioReadHandler;
            this._portfolioDtoCreator = portfolioDtoCreator;
            this._portfolioUpdateHandler = portfolioUpdateHandler;
            this._userClaimsProcessor = userClaimsProcessor;
        }

        [Route("{id}")]
        public HttpResponseMessage Delete(Guid id)
        {
            var userClaims = _userClaimsProcessor.GetPortfolioClaims();
            this._portfolioUpdateHandler.DeletePortfolio(id, userClaims);

            // invalidate cache for parent resource
            var cache = this.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(this.Request);
            cache.RemoveStartsWith(
                this.Configuration.CacheOutputConfiguration().MakeBaseCachekey("Portfolios", "Get"));
            return this.Request.CreateResponse(HttpStatusCode.OK);
        }

        [Route]
        public HttpResponseMessage Get(
            Guid userId,
            bool onlyAutomatic,
            bool includeAllFields)
        {
            var userClaims = _userClaimsProcessor.GetPortfolioClaims();

            var filter = new PortfolioFilter
            {
                SharingStatus = onlyAutomatic ? PortfolioSharingStatus.Shared : PortfolioSharingStatus.Private,
                PortfolioType = PortfolioTypes.System,
                AddedByUserId = userClaims.UserId,
                AccountId = userClaims.AccountId,
            };

            var portfolios = this._portfolioReadHandler.GetPortfolios(filter);

            var portfolioList =
                portfolios.Select(p => this._portfolioDtoCreator.Create(p, userClaims.UserName, includeAllFields));

            return this.Request.CreateResponse(HttpStatusCode.OK, portfolioList);
        }

        [Route("{id}")]
        public HttpResponseMessage Get([FromUri]Guid id, Guid userId)
        {
            var userClaims = _userClaimsProcessor.GetPortfolioClaims();
            var portfolio = this._portfolioReadHandler.GetPortfolioById(id, userClaims);
            var portfolioDto = this._portfolioDtoCreator.Create(portfolio, userClaims.UserName, true);
            return this.Request.CreateResponse(HttpStatusCode.OK, portfolioDto);
        }

        [Route("{id}")]
        public HttpResponseMessage Patch([FromUri] Guid id, RelateduniversesDto[] relatedUniverses)
        {
            if (id == Guid.Empty)
            {
                throw new PortfolioIdNullException();
            }

            if (relatedUniverses.IsNullOrEmpty())
            {
                throw new PortfolioNameNullException();
            }

            PortfolioUserClaims userClaims = _userClaimsProcessor.GetPortfolioClaims();

            this._portfolioUpdateHandler.SetAssociatedUniverses(
                id,
                relatedUniverses.Select(x => x.Id).ToArray(),
                userClaims);

            // invalidate cache for parent resource
            var cache = this.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(this.Request);
            cache.RemoveStartsWith(
                this.Configuration.CacheOutputConfiguration().MakeBaseCachekey("Portfolios", "Get"));

            return this.Request.CreateResponse(HttpStatusCode.OK);
        }

        [Route]
        public HttpResponseMessage Post([FromBody]SystemPortfolioDto systemPortfolioDto)
        {
            if (systemPortfolioDto.Relateduniverses.IsNullOrEmpty())
            {
                throw new PortfolioEmptyConstituentsList();
            }

            PortfolioUserClaims userClaims = _userClaimsProcessor.GetPortfolioClaims();

            var result = this._portfolioUpdateHandler.Add(
                systemPortfolioDto.Name,
                systemPortfolioDto.Relateduniverses.Select(x => x.Id).ToArray(),
                userClaims);

            // invalidate cache for parent resource
            var cache = this.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(this.Request);
            cache.RemoveStartsWith(
                this.Configuration.CacheOutputConfiguration().MakeBaseCachekey("Portfolios", "Get"));

            return this.Request.CreateResponse(HttpStatusCode.Created, result);
        }

        public class SystemPortfolioDto
        {
            public string Name { get; set; }

            public RelateduniversesDto[] Relateduniverses { get; set; }
        }
    }
}
